#include "GraphicsRenderer.h"

#include <atlstr.h>
#include "ShaderData.h"

#define VIEWDIMENSION D3D11_DSV_DIMENSION_TEXTURE2DMS

#define COMPILE_COLORED_LINE_SHADERS
#define ANTI_ALIASING_COUNT (1)
#define ANTI_ALIASING_QUALITY (0)
#define TEXTURE_FORMAT DXGI_FORMAT_R8G8B8A8_UNORM
#define SHADERMODEL "vs_4_0"

using namespace DirectX;

GraphicsRenderer::GraphicsRenderer(HWND windowHandle, XMFLOAT3 lightPosition1, XMFLOAT3 lightPosition2) : m_d3dDevice(nullptr),
	m_d3dContext(nullptr),
	m_swapChain(nullptr),
	m_renderTargetView(nullptr),
	m_depthStencilBuffer(nullptr),
	m_depthStencilView(nullptr),
	m_lineVertexShader(nullptr),
	m_linePixelShader(nullptr), 
	m_vertexShader(nullptr),
	m_pixelShader(nullptr),
	m_shadowMapVertexShader(nullptr),
	m_shadowMapPixelShader(nullptr),
	m_shaderInputLayout(nullptr),
	m_coloredLineShaderInputLayout(nullptr),
	m_cbView(nullptr),
	m_cbProjection(nullptr),
	m_cbModel(nullptr),
	m_cbLight1(nullptr),
	m_cbLight2(nullptr),
	m_cbOptions(nullptr),
	m_samplerStateLinear(nullptr),
	m_samplerStateClamp(nullptr),
	m_ModelColor(XMFLOAT4( 0.7f, 0.7f, 0.7f, 1.0f )),
	m_windowHandle(windowHandle),
	m_camera(nullptr),
	m_pointLight1(XMFLOAT4(0.25f, 0.25f, 0.5f, 1.f), XMFLOAT4(1.f, 1.f, 0.75f, 1.f), lightPosition1, XMFLOAT3(0.f, 0.f, 0.f), M_PROJECTION_NEAR, M_PROJECTION_FAR),
	m_shadowMap1Renderer(),
	m_pointLight2(XMFLOAT4(0.05f, 0.05f, 0.f, 1.f), XMFLOAT4(0.5f, 0.5f, 0.5f, 1.f), lightPosition2, XMFLOAT3(0.f, 0.f, 0.f), M_PROJECTION_NEAR, M_PROJECTION_FAR),
	m_shadowMap2Renderer(),
	m_standartRasterizerState(nullptr),
	m_wireFrameRasterizerState(nullptr),
	m_rasterizerStateActual(nullptr),
	m_useBumpMaps(false),
	m_bumpiness(1.f),
	m_ray(nullptr),
	m_kdTree(nullptr),
	m_showKdTree(false),
	m_textures(),
	m_antiAliasingCount(ANTI_ALIASING_COUNT),
	m_antiAliasingQuality(ANTI_ALIASING_QUALITY),
	m_measurementLine(nullptr),
	m_antiAliasing(true)
{
	RECT windowRectangle;
	GetClientRect( m_windowHandle, &windowRectangle );
	m_windowWith = windowRectangle.right - windowRectangle.left;
	m_windowHeight = windowRectangle.bottom - windowRectangle.top;
	if(FAILED(m_InitializeD3D()))
	{
		MessageBox( nullptr,
			L"Direct3D initialization failed",
			L"Error",
			MB_OK );
		exit(EXIT_FAILURE);
	}
}

const float GraphicsRenderer::M_PROJECTION_FAR = 500.f;
const float GraphicsRenderer::M_PROJECTION_NEAR = 1.f;
const int GraphicsRenderer::M_SHADOW_MAP_HEIGHT = 1024;
const int GraphicsRenderer::M_SHADOW_MAP_WIDTH = 1024;

HRESULT GraphicsRenderer::m_InitializeD3D( void )
{
	HRESULT result = S_OK;
	UINT createDeviceFlags = 0;
#ifdef _DEBUG
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	D3D_DRIVER_TYPE driverTypes[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	UINT numDriverTypes = ARRAYSIZE( driverTypes );

	D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
	};
	UINT numFeatureLevels = ARRAYSIZE( featureLevels );
	UINT antiAliasingCount = m_antiAliasing ? m_antiAliasingCount : 1;
	UINT antiAliasingQuality = m_antiAliasing ? m_antiAliasingQuality : 0;
	DXGI_SWAP_CHAIN_DESC swapChainDesc;
	ZeroMemory( &swapChainDesc, sizeof( swapChainDesc ) );
	swapChainDesc.BufferCount = 1;
	swapChainDesc.BufferDesc.Width = m_windowWith;
	swapChainDesc.BufferDesc.Height = m_windowHeight;
	swapChainDesc.BufferDesc.Format = TEXTURE_FORMAT;
	swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
	swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	swapChainDesc.OutputWindow = m_windowHandle;
	swapChainDesc.SampleDesc.Count = antiAliasingCount;
	swapChainDesc.SampleDesc.Quality = antiAliasingQuality;
	swapChainDesc.Windowed = TRUE;
	D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_NULL;
	D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_1;
	for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
	{
		driverType = driverTypes[driverTypeIndex];
		result = D3D11CreateDeviceAndSwapChain( nullptr,
			driverType,
			nullptr,
			createDeviceFlags,
			featureLevels,
			numFeatureLevels,
			D3D11_SDK_VERSION,
			&swapChainDesc,
			&m_swapChain,
			&m_d3dDevice,
			&featureLevel,
			&m_d3dContext );
		if ( result == E_INVALIDARG )
		{
			// DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
			result = D3D11CreateDeviceAndSwapChain( nullptr,
				driverType,
				nullptr,
				createDeviceFlags,
				&featureLevels[1],
				numFeatureLevels - 1,
				D3D11_SDK_VERSION,
				&swapChainDesc, 
				&m_swapChain,
				&m_d3dDevice,
				&featureLevel,
				&m_d3dContext );
		}
		if( SUCCEEDED( result ) )
		{
			break;
		}
	}
	if( FAILED( result ) )
	{
		return result;
	}
	// Create a render target view
	ID3D11Texture2D* backBuffer = nullptr;
	result = m_swapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&backBuffer );
	if( FAILED( result ) )
	{
		return result;
	}
	result = m_d3dDevice->CreateRenderTargetView( backBuffer, nullptr, &m_renderTargetView );
	backBuffer->Release();
	if( FAILED( result ) )
	{
		return result;
	}
	// Create depth stencil texture
	D3D11_TEXTURE2D_DESC depthStencilBufferDesc;
	ZeroMemory( &depthStencilBufferDesc, sizeof(depthStencilBufferDesc) );
	depthStencilBufferDesc.Width = m_windowWith;
	depthStencilBufferDesc.Height = m_windowHeight;
	depthStencilBufferDesc.MipLevels = 1;
	depthStencilBufferDesc.ArraySize = 1;
	depthStencilBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthStencilBufferDesc.SampleDesc.Count = antiAliasingCount;
	depthStencilBufferDesc.SampleDesc.Quality = antiAliasingQuality;
	depthStencilBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	depthStencilBufferDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthStencilBufferDesc.CPUAccessFlags = 0;
	depthStencilBufferDesc.MiscFlags = 0;
	result = m_d3dDevice->CreateTexture2D( &depthStencilBufferDesc, nullptr, &m_depthStencilBuffer );
	if( FAILED( result ) )
	{
		return result;
	}
	// Create the depth stencil view
	D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
	ZeroMemory( &depthStencilViewDesc, sizeof(depthStencilViewDesc) );
	depthStencilViewDesc.Format = depthStencilBufferDesc.Format;
	depthStencilViewDesc.ViewDimension = VIEWDIMENSION;
	depthStencilViewDesc.Texture2D.MipSlice = 0;
	result = m_d3dDevice->CreateDepthStencilView( m_depthStencilBuffer, &depthStencilViewDesc, &m_depthStencilView );
	if( FAILED( result ) )
	{
		return result;
	}
	m_ResetRenderTarget();

	// Create the vertex shaders
	ID3DBlob* vertexShaderBinary = nullptr;
	result = m_CompileShaderFromFile( L"PathSimulation.fx", "VS", SHADERMODEL, &vertexShaderBinary );
	if( FAILED( result ) )
	{
		MessageBox( nullptr,
			L"The FX function PathSimulation.fx - VS cannot be compiled.  Please run this executable from the directory that contains the FX file.",
			L"Error",
			MB_OK );
		return result;
	}
	result = m_d3dDevice->CreateVertexShader( vertexShaderBinary->GetBufferPointer(),
		vertexShaderBinary->GetBufferSize(),
		nullptr,
		&m_vertexShader );
	if( FAILED( result ) )
	{    
		vertexShaderBinary->Release();
		return result;
	}
	result = m_CompileShaderFromFile( L"ShadowMap.fx", "VS", SHADERMODEL, &vertexShaderBinary );
	if( FAILED( result ) )
	{
		MessageBox( nullptr,
			L"The FX function ShadowMap.fx - VS cannot be compiled.  Please run this executable from the directory that contains the FX file.",
			L"Error",
			MB_OK );
		return result;
	}
	result = m_d3dDevice->CreateVertexShader( vertexShaderBinary->GetBufferPointer(),
		vertexShaderBinary->GetBufferSize(),
		nullptr,
		&m_shadowMapVertexShader );
	if( FAILED( result ) )
	{    
		vertexShaderBinary->Release();
		return result;
	}



	// Define the input layout
	D3D11_INPUT_ELEMENT_DESC shaderInputLayout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};
	UINT numElements = ARRAYSIZE( shaderInputLayout );

	// Create the input layout
	result = m_d3dDevice->CreateInputLayout( shaderInputLayout,
		numElements,
		vertexShaderBinary->GetBufferPointer(),
		vertexShaderBinary->GetBufferSize(),
		&m_shaderInputLayout );
	vertexShaderBinary->Release();
	if( FAILED( result ) )
	{
		MessageBox( nullptr,
			L"Unable to creat input layout. Look for discrepancies between shader input data types.",
			L"Error",
			MB_OK );
		return result;
	}
	// Set the input layout
	m_d3dContext->IASetInputLayout( m_shaderInputLayout );


#ifdef COMPILE_COLORED_LINE_SHADERS
	result = m_CompileShaderFromFile( L"ColoredLine.fx", "VS", SHADERMODEL, &vertexShaderBinary );
	if( FAILED( result ) )
	{
		MessageBox( nullptr,
			L"The FX function ColoredLine.fx - VS cannot be compiled.  Please run this executable from the directory that contains the FX file.",
			L"Error",
			MB_OK );
		return result;
	}
	result = m_d3dDevice->CreateVertexShader( vertexShaderBinary->GetBufferPointer(),
		vertexShaderBinary->GetBufferSize(),
		nullptr,
		&m_lineVertexShader );
	if( FAILED( result ) )
	{    
		vertexShaderBinary->Release();
		return result;
	}  
	D3D11_INPUT_ELEMENT_DESC coloredLineShaderInputLayout[] = 
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};
	numElements = ARRAYSIZE( coloredLineShaderInputLayout );
	result = m_d3dDevice->CreateInputLayout( coloredLineShaderInputLayout,
		numElements,
		vertexShaderBinary->GetBufferPointer(),
		vertexShaderBinary->GetBufferSize(),
		&m_coloredLineShaderInputLayout );
	vertexShaderBinary->Release();
	if( FAILED( result ) )
	{
		MessageBox( nullptr,
			L"Unable to creat input layout. Look for discrepancies between shader input data types.",
			L"Error",
			MB_OK );
		return result;
	}
#endif // COMPILE_COLORED_LINE_SHADERS

	// Create the pixel shaders
	ID3DBlob* pixelShaderBinary = nullptr;
	result = m_CompileShaderFromFile( L"PathSimulation.fx", "PS", "ps_4_0", &pixelShaderBinary );
	if( FAILED( result ) )
	{
		MessageBox( nullptr,
			L"The FX file PathSimulation.fx - PS cannot be compiled.  Please run this executable from the directory that contains the FX file.",
			L"Error",
			MB_OK );
		return result;
	}
	result = m_d3dDevice->CreatePixelShader( pixelShaderBinary->GetBufferPointer(), pixelShaderBinary->GetBufferSize(), nullptr, &m_pixelShader );
	pixelShaderBinary->Release();
	if( FAILED( result ) )
	{
		return result;
	}
	result = m_CompileShaderFromFile( L"ShadowMap.fx", "PS", "ps_4_0", &pixelShaderBinary );
	if( FAILED( result ) )
	{
		MessageBox( nullptr,
			L"The FX file PathSimulation.fx - PS cannot be compiled.  Please run this executable from the directory that contains the FX file.",
			L"Error",
			MB_OK );
		return result;
	}
	result = m_d3dDevice->CreatePixelShader( pixelShaderBinary->GetBufferPointer(), pixelShaderBinary->GetBufferSize(), nullptr, &m_shadowMapPixelShader );
	pixelShaderBinary->Release();
	if( FAILED( result ) )
	{
		return result;
	}
#ifdef COMPILE_COLORED_LINE_SHADERS
	result = m_CompileShaderFromFile( L"ColoredLine.fx", "PS", "ps_4_0", &pixelShaderBinary );
	if( FAILED( result ) )
	{
		MessageBox( nullptr,
			L"The FX file ColoredLine.fx - PS cannot be compiled.  Please run this executable from the directory that contains the FX file.",
			L"Error",
			MB_OK );
		return result;
	}
	result = m_d3dDevice->CreatePixelShader( pixelShaderBinary->GetBufferPointer(), pixelShaderBinary->GetBufferSize(), nullptr, &m_linePixelShader );
	pixelShaderBinary->Release();
	if( FAILED( result ) )
	{
		return result;
	}  
#endif // COMPILE_COLORED_LINE_SHADERS


	// Create the constant buffers
	D3D11_BUFFER_DESC bufferDesc;
	ZeroMemory(&bufferDesc, sizeof(bufferDesc));
	bufferDesc.Usage = D3D11_USAGE_DEFAULT;
	bufferDesc.ByteWidth = sizeof(CBView);
	bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bufferDesc.CPUAccessFlags = 0;
	result = m_d3dDevice->CreateBuffer( &bufferDesc, nullptr, &m_cbView );
	if( FAILED( result ) )
	{
		return result;
	}
	bufferDesc.ByteWidth = sizeof(CBProjection);
	result = m_d3dDevice->CreateBuffer( &bufferDesc, nullptr, &m_cbProjection );
	if( FAILED( result ) )
	{
		return result;
	}
	bufferDesc.ByteWidth = sizeof(CBModel);
	result = m_d3dDevice->CreateBuffer( &bufferDesc, nullptr, &m_cbModel );
	if( FAILED( result ) )
	{
		return result;
	}
	bufferDesc.ByteWidth = sizeof(CBLight);
	result = m_d3dDevice->CreateBuffer( &bufferDesc, nullptr, &m_cbLight1 );
	if( FAILED( result ) )
	{
		return result;
	}
	result = m_d3dDevice->CreateBuffer( &bufferDesc, nullptr, &m_cbLight2 );
	if( FAILED( result ) )
	{
		return result;
	}
	bufferDesc.ByteWidth = sizeof(CBOptions);
	result = m_d3dDevice->CreateBuffer( &bufferDesc, nullptr, &m_cbOptions );
	if( FAILED( result ) )
	{
		return result;
	}
	// Create the sample state
	D3D11_SAMPLER_DESC samplerDesc;
	ZeroMemory( &samplerDesc, sizeof(samplerDesc) );
	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.MipLODBias = 0.0f;
	samplerDesc.MaxAnisotropy = 1;
	samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	samplerDesc.MinLOD = 0;
	samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
	result = m_d3dDevice->CreateSamplerState( &samplerDesc, &m_samplerStateLinear );
	if( FAILED( result ) )
	{
		return result;
	}
	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	result = m_d3dDevice->CreateSamplerState( &samplerDesc, &m_samplerStateClamp );
	if( FAILED( result ) )
	{
		return result;
	}
	// Initialize the projection matrix
	m_projectionMatrix = XMMatrixPerspectiveFovLH( XM_PIDIV4, static_cast<float>(m_windowWith) / static_cast<float>(m_windowHeight), M_PROJECTION_NEAR, M_PROJECTION_FAR);
	// Fill light constant buffers
	CBLight cbLight1;
	cbLight1.DiffuseColor = m_pointLight1.GetDiffuseColor();
	cbLight1.AmbientColor = m_pointLight1.GetAmbientColor();
	cbLight1.LightView = XMMatrixTranspose(m_pointLight1.GetViewMatrix()); 
	cbLight1.LightProjection = XMMatrixTranspose(m_pointLight1.GetProjectionMatrix());
	XMFLOAT3 pos1 = m_pointLight1.GetPosition();
	cbLight1.Pos = XMFLOAT4(pos1.x, pos1.y, pos1.z, 1);
	m_d3dContext->UpdateSubresource( m_cbLight1, 0, nullptr, &cbLight1, 0, 0);

	CBLight cbLight2;
	cbLight2.DiffuseColor = m_pointLight2.GetDiffuseColor();
	cbLight2.AmbientColor = m_pointLight2.GetAmbientColor();
	cbLight2.LightView = XMMatrixTranspose(m_pointLight2.GetViewMatrix());
	cbLight2.LightProjection = XMMatrixTranspose(m_pointLight2.GetProjectionMatrix());
	XMFLOAT3 pos2 = m_pointLight2.GetPosition();
	cbLight2.Pos = XMFLOAT4(pos2.x, pos2.y, pos2.z, 1);
	m_d3dContext->UpdateSubresource( m_cbLight2, 0, nullptr, &cbLight2, 0, 0);

	// Global render Setup ( set triangle list as render input method )
	m_d3dContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	// Initialize shadow map renderer
	//Save default and wireframe rasterizer states
	D3D11_RASTERIZER_DESC  rasterizerDesc;
	rasterizerDesc.FillMode = D3D11_FILL_SOLID;
	rasterizerDesc.CullMode = D3D11_CULL_FRONT;
	rasterizerDesc.FrontCounterClockwise = true;
	rasterizerDesc.DepthBias = false;
	rasterizerDesc.DepthBiasClamp = 0;
	rasterizerDesc.SlopeScaledDepthBias = 0;
	rasterizerDesc.DepthClipEnable = true;
	rasterizerDesc.ScissorEnable = false;
	rasterizerDesc.MultisampleEnable = false;
	rasterizerDesc.AntialiasedLineEnable = true;
	m_d3dDevice->CreateRasterizerState(&rasterizerDesc, &m_standartRasterizerState);
	m_d3dContext->RSSetState(m_standartRasterizerState);
	m_rasterizerStateActual = m_standartRasterizerState;
	rasterizerDesc.FillMode = D3D11_FILL_WIREFRAME;
	rasterizerDesc.CullMode = D3D11_CULL_NONE;
	m_d3dDevice->CreateRasterizerState(&rasterizerDesc, &m_wireFrameRasterizerState);
	//shaddow mapping stuff
	result = m_shadowMap1Renderer.InitializeD3D(m_d3dDevice, M_SHADOW_MAP_WIDTH, M_SHADOW_MAP_HEIGHT);
	if(FAILED(result))
	{
		return result;
	}
	result = m_shadowMap2Renderer.InitializeD3D(m_d3dDevice, M_SHADOW_MAP_WIDTH, M_SHADOW_MAP_HEIGHT);
	if(FAILED(result))
	{
		return result;
	}
	return S_OK;
}


GraphicsRenderer::~GraphicsRenderer(void)
{
	m_ClearD3D();
}

void GraphicsRenderer::Render(void) const
{
	//render shadow maps
	m_SetShadowMapShaders();
	m_shadowMap1Renderer.SetRenderTarget(m_d3dContext);
	m_shadowMap1Renderer.ClearRenderTarget(m_d3dContext);
	//--------------------------------------------------------------------------------------------------------------------------render shadow map 1
	std::vector<Model*>::const_iterator it;
	for(it = m_models.begin(); it != m_models.end(); ++it)
	{
		CBModel cbModel;
		const ModelRenderData *renderData = (*it)->GetRenderData();
		cbModel.TransformMatrix = XMMatrixTranspose(renderData->Transform);
		cbModel.Color = renderData->Color;
		m_d3dContext->UpdateSubresource( m_cbModel, 0, nullptr, &cbModel, 0, 0);
		m_d3dContext->IASetVertexBuffers(0, 1, &renderData->VertexBuffer, &renderData->vertexStride, &renderData->vertexOffset);
		m_d3dContext->IASetIndexBuffer( renderData->IndexBuffer, DXGI_FORMAT_R16_UINT, 0 );
		m_d3dContext->DrawIndexed( renderData->numIndizesToDraw, 0, 0 );
	}
	//--------------------------------------------------------------------------------------------------------------------------render shadow map 2
	m_shadowMap2Renderer.SetRenderTarget(m_d3dContext);
	m_shadowMap2Renderer.ClearRenderTarget(m_d3dContext);
	for(it = m_models.begin(); it != m_models.end(); ++it)
	{
		CBModel cbModel;
		const ModelRenderData *renderData = (*it)->GetRenderData();
		cbModel.TransformMatrix = XMMatrixTranspose(renderData->Transform);
		//cbModel.TransformMatrix = renderData->Transform;
		cbModel.Color = renderData->Color;
		m_d3dContext->UpdateSubresource( m_cbModel, 0, nullptr, &cbModel, 0, 0);
		m_d3dContext->IASetVertexBuffers(0, 1, &renderData->VertexBuffer, &renderData->vertexStride, &renderData->vertexOffset);
		m_d3dContext->IASetIndexBuffer( renderData->IndexBuffer, DXGI_FORMAT_R16_UINT, 0 );
		m_d3dContext->DrawIndexed( renderData->numIndizesToDraw, 0, 0 );
	}
	//--------------------------------------------------------------------------------------------------------------------------render to screen
	//reset buffers from last render call
	m_ResetRenderTarget();
	m_d3dContext->ClearRenderTargetView( m_renderTargetView, Colors::MidnightBlue );
	m_d3dContext->ClearDepthStencilView( m_depthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0 );

	m_SetStandartShaders();	

	CBOptions cbOptions;
	if(m_useBumpMaps)
	{
		cbOptions.bumpiness = m_bumpiness;
	}
	else
	{
		cbOptions.bumpiness = 0.f;
	}
	m_d3dContext->UpdateSubresource( m_cbOptions, 0, nullptr, &cbOptions, 0, 0);
	for(it = m_models.begin(); it != m_models.end(); ++it)
	{
		CBModel cbModel;
		const ModelRenderData *renderData = (*it)->GetRenderData();
		m_d3dContext->PSSetShaderResources( 0, 1, renderData->diffuseColor->GetResourceView() );
		m_d3dContext->PSSetShaderResources( 3, 1, renderData->normalMap->GetResourceView() );
		cbModel.TransformMatrix = XMMatrixTranspose(renderData->Transform);
		cbModel.Color = renderData->Color;
		m_d3dContext->UpdateSubresource( m_cbModel, 0, nullptr, &cbModel, 0, 0);
		m_d3dContext->IASetVertexBuffers(0, 1, &renderData->VertexBuffer, &renderData->vertexStride, &renderData->vertexOffset);
		m_d3dContext->IASetIndexBuffer( renderData->IndexBuffer, DXGI_FORMAT_R16_UINT, 0 );
		m_d3dContext->DrawIndexed( renderData->numIndizesToDraw, 0, 0 );
	}

	//-----------------------------------------------------------------------------------------------------------------------------------draw colored lines
	m_SetLineShaders();

	m_d3dContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
	if(m_ray)
	{
		m_d3dContext->IASetVertexBuffers(0, 1, &m_ray->m_renderData.VertexBuffer, &m_ray->m_renderData.VertexStride, &m_ray->m_renderData.VertexOffset);
		m_d3dContext->Draw(m_ray->m_numVertices, 0);
	}
	if(m_measurementLine)
	{
		m_d3dContext->IASetVertexBuffers(0, 1, &m_measurementLine->m_renderData.VertexBuffer, &m_measurementLine->m_renderData.VertexStride, &m_measurementLine->m_renderData.VertexOffset);
		m_d3dContext->Draw(m_measurementLine->m_numVertices, 0);
	}
	if(m_kdTree && m_showKdTree)
	{
		m_d3dContext->IASetVertexBuffers(0, 1, &m_kdTree->m_renderData.VertexBuffer, &m_kdTree->m_renderData.VertexStride, &m_kdTree->m_renderData.VertexOffset);
		m_d3dContext->Draw(m_kdTree->m_numVertices, 0);
	}

	//done, swap back and front buffer
	m_swapChain->Present( 0, 0 );

	//release shader resource views
	ID3D11ShaderResourceView* srvs[3] = {0};
	m_d3dContext->PSSetShaderResources(0, 3, srvs);
}

HRESULT GraphicsRenderer::m_CompileShaderFromFile( WCHAR* shaderFileName, LPCSTR shaderEntryPointName, LPCSTR shaderModel, ID3DBlob** shaderBinaryOut )
{
	HRESULT result = S_OK;
	DWORD shaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
	shaderFlags |= D3DCOMPILE_DEBUG;
#endif
	ID3DBlob* errorBinary;
	result = D3DCompileFromFile( shaderFileName,
		nullptr,
		nullptr,
		shaderEntryPointName,
		shaderModel, 
		shaderFlags,
		0,
		shaderBinaryOut,
		&errorBinary );
	if( FAILED(result) )
	{
		if( errorBinary != nullptr )
		{
			char* errMsg = static_cast<char*>(errorBinary->GetBufferPointer());
			OutputDebugStringA( errMsg );
		}
		if( errorBinary ) errorBinary->Release();
		{
			return result;
		}
	}
	if( errorBinary ) errorBinary->Release();
	return S_OK;
}

HRESULT GraphicsRenderer::AddModel( Model& model )
{
	HRESULT result = model.InitializeD3D(m_d3dDevice);
	if(result != S_OK)
	{
		return result;
	}
	Texture* modelTexture = model.GetRenderData()->diffuseColor;
	if(!modelTexture->IsD3DInitialized())
	{
		result = AddTexture(*modelTexture);
		if(FAILED(result))
		{
			return result;
		}
	}
	modelTexture = model.GetRenderData()->normalMap;
	if(!modelTexture->IsD3DInitialized())
	{
		result = AddTexture(*modelTexture);
		if(FAILED(result))
		{
			return result;
		}
	}
	m_models.push_back(&model);	
	return S_OK;
}

HRESULT GraphicsRenderer::AddLineList( LineList& lineList )
{
	HRESULT result = lineList.InitializeD3D(m_d3dDevice);
	if(result != S_OK)
	{
		return result;
	}
	m_lineLists.push_back(&lineList);
	return S_OK;
}

HRESULT GraphicsRenderer::AddTexture( Texture& texture)
{
	m_textures.push_back(&texture);
	return texture.InitializeD3D(m_d3dDevice);
}

void GraphicsRenderer::SetCamera( Camera* camera )
{
	m_camera = camera;
}


void GraphicsRenderer::m_ResetRenderTarget(void) const
{
	m_d3dContext->OMSetRenderTargets( 1, &m_renderTargetView, m_depthStencilView );
	static const D3D11_VIEWPORT viewport = { 0, 0, static_cast<float>(m_windowWith), static_cast<float>(m_windowHeight), 0.0f, 1.0f };
	m_d3dContext->RSSetViewports( 1, &viewport );
}

void GraphicsRenderer::m_SetStandartShaders(void) const
{
	m_d3dContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

	m_d3dContext->IASetInputLayout( m_shaderInputLayout );
	m_d3dContext->VSSetShader( m_vertexShader, nullptr, 0 );
	m_d3dContext->VSSetConstantBuffers( 0, 1, &m_cbView );
	m_d3dContext->VSSetConstantBuffers( 1, 1, &m_cbProjection );
	m_d3dContext->VSSetConstantBuffers( 2, 1, &m_cbModel );
	m_d3dContext->VSSetConstantBuffers( 3, 1, &m_cbLight1 );
	m_d3dContext->VSSetConstantBuffers( 4, 1, &m_cbLight2 );

	m_d3dContext->PSSetShader( m_pixelShader, nullptr, 0 );
	m_d3dContext->PSSetConstantBuffers( 2, 1, &m_cbModel );
	m_d3dContext->PSSetConstantBuffers( 3, 1, &m_cbLight1 );
	m_d3dContext->PSSetConstantBuffers( 4, 1, &m_cbLight2 );
	m_d3dContext->PSSetConstantBuffers( 5, 1, &m_cbOptions );
	m_d3dContext->PSSetSamplers( 0, 1, &m_samplerStateLinear );
	m_d3dContext->PSSetSamplers(1, 1, &m_samplerStateClamp );

	CBView cbView;
	CBProjection cbProjection;
	XMMATRIX cameraViewMatrix = m_camera->GetViewMatrix();
	cbView.ViewMatrix = XMMatrixTranspose( cameraViewMatrix );
	m_d3dContext->UpdateSubresource( m_cbView, 0, nullptr, &cbView, 0, 0 );
	cbProjection.ProjectionMatrix = XMMatrixTranspose( m_projectionMatrix );
	m_d3dContext->UpdateSubresource( m_cbProjection, 0, nullptr, &cbProjection, 0, 0 );

	ID3D11ShaderResourceView*const* shadowMap1 = m_shadowMap1Renderer.GetShaderResourceView();
	ID3D11ShaderResourceView*const* shadowMap2 = m_shadowMap2Renderer.GetShaderResourceView();
	m_d3dContext->PSSetShaderResources(1, 1, shadowMap1);
	m_d3dContext->PSSetShaderResources(2, 1, shadowMap2);
}

void GraphicsRenderer::m_SetShadowMapShaders(void) const
{
	m_d3dContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

	m_d3dContext->IASetInputLayout( m_shaderInputLayout );
	m_d3dContext->VSSetShader( m_shadowMapVertexShader, nullptr, 0 );
	m_d3dContext->VSSetConstantBuffers( 0, 1, &m_cbView );
	m_d3dContext->VSSetConstantBuffers( 1, 1, &m_cbProjection );
	m_d3dContext->VSSetConstantBuffers( 2, 1, &m_cbModel );

	m_d3dContext->PSSetShader( m_shadowMapPixelShader, nullptr, 0 );
	m_d3dContext->PSSetConstantBuffers( 2, 1, &m_cbModel );

	CBView cbView;
	CBProjection cbProjection;
	XMMATRIX pointLightViewMatrix = m_pointLight1.GetViewMatrix();
	cbView.ViewMatrix = XMMatrixTranspose( pointLightViewMatrix );
	m_d3dContext->UpdateSubresource( m_cbView, 0, nullptr, &cbView, 0, 0 );
	XMMATRIX pointLightProjectionMatrix = m_pointLight1.GetProjectionMatrix();
	cbProjection.ProjectionMatrix = XMMatrixTranspose( pointLightProjectionMatrix );
	m_d3dContext->UpdateSubresource( m_cbProjection, 0, nullptr, &cbProjection, 0, 0 );
}

void GraphicsRenderer::m_SetLineShaders(void) const
{
	m_d3dContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_LINELIST );

	m_d3dContext->IASetInputLayout( m_coloredLineShaderInputLayout );
	m_d3dContext->VSSetShader( m_lineVertexShader, nullptr, 0 );
	m_d3dContext->VSSetConstantBuffers( 0, 1, &m_cbView );
	m_d3dContext->VSSetConstantBuffers( 1, 1, &m_cbProjection );

	m_d3dContext->PSSetShader( m_linePixelShader, nullptr, 0 );
	m_d3dContext->PSSetSamplers( 0, 1, &m_samplerStateLinear );

	CBView cbView;
	CBProjection cbProjection;
	XMMATRIX cameraViewMatrix = m_camera->GetViewMatrix();
	cbView.ViewMatrix = XMMatrixTranspose( cameraViewMatrix );
	m_d3dContext->UpdateSubresource( m_cbView, 0, nullptr, &cbView, 0, 0 );
	cbProjection.ProjectionMatrix = XMMatrixTranspose( m_projectionMatrix );
	m_d3dContext->UpdateSubresource( m_cbProjection, 0, nullptr, &cbProjection, 0, 0 );
}

void GraphicsRenderer::SwitchUseBumpMap( void )
{
	m_useBumpMaps = !m_useBumpMaps;
	if(!m_useBumpMaps)
	{
		static ID3D11ShaderResourceView* noBumpMap[1] = {nullptr};
		m_d3dContext->PSSetShaderResources( 3, 1, noBumpMap);
	}
}

void GraphicsRenderer::ApplyBumpinessChange( int change )
{
	if(change > 0)
	{
		for(int i = 0; i < change; ++i)
		{
			m_bumpiness *= 2.f;
		}
	}
	else
	{
		for(int i = 0; i > change; --i)
		{
			m_bumpiness /= 2.f;
		}
	}
}

void GraphicsRenderer::SwitchRasterizer( void )
{
	ID3D11RasterizerState* newState = nullptr;
	if(m_rasterizerStateActual == m_standartRasterizerState)
	{
		newState = m_wireFrameRasterizerState;
	}
	else
	{
		newState = m_standartRasterizerState;
	}
	m_d3dContext->RSSetState(newState);
	m_rasterizerStateActual = newState;
}

void GraphicsRenderer::ApplyVertexChanges( Model* model )
{
	model->UpdateVertexBuffer(m_d3dContext);
}

std::vector<Model*> GraphicsRenderer::GetRegisteredModels( void )
{
	return m_models;
}

void GraphicsRenderer::UpdateRay( LineList* ray )
{
	if(m_ray == ray)
	{
		HRESULT lineListUpdated = ray->UpdateD3d(m_d3dContext);
		if(lineListUpdated != S_OK)
		{
			ray->InitializeD3D(m_d3dDevice);
		}
	}
	else
	{ 
		ray->InitializeD3D(m_d3dDevice);
		m_ray = ray;
	}
}

void GraphicsRenderer::UpdateMeasurementLine( LineList* measurementLine )
{
	if(m_measurementLine == measurementLine)
	{
		HRESULT lineListUpdated = measurementLine->UpdateD3d(m_d3dContext);
		if(lineListUpdated != S_OK)
		{
			measurementLine->InitializeD3D(m_d3dDevice);
		}
	}
	else
	{ 
		measurementLine->InitializeD3D(m_d3dDevice);
		m_measurementLine = measurementLine;
	}
}

void GraphicsRenderer::UpdateKdTree( LineList* kdTree )
{
	if(m_kdTree == kdTree)
	{
		HRESULT lineListUpdated = kdTree->UpdateD3d(m_d3dContext);
		if(lineListUpdated != S_OK)
		{
			kdTree->InitializeD3D(m_d3dDevice);
		}
	}
	else
	{ 
		kdTree->InitializeD3D(m_d3dDevice);
		m_kdTree = kdTree;
	}
}

void GraphicsRenderer::ToggleShowKdTree()
{
	m_showKdTree = !m_showKdTree;
}

void GraphicsRenderer::m_ClearD3D( void )
{
	if( m_d3dContext ) m_d3dContext->ClearState();
	if( m_samplerStateLinear ) m_samplerStateLinear->Release();
	m_samplerStateLinear = nullptr;
	if( m_samplerStateClamp ) m_samplerStateClamp->Release();
	m_samplerStateClamp = nullptr;
	if( m_cbView ) m_cbView->Release();
	m_cbView = nullptr;
	if( m_cbProjection ) m_cbProjection->Release();
	m_cbProjection = nullptr;
	if( m_cbModel ) m_cbModel->Release();
	m_cbModel = nullptr;
	if( m_cbLight1 ) m_cbLight1->Release();
	m_cbLight1 = nullptr;
	if( m_cbLight2 ) m_cbLight2->Release();
	m_cbLight2 = nullptr;
	if( m_cbOptions ) m_cbOptions->Release();
	m_cbOptions = nullptr;
	if( m_shaderInputLayout ) m_shaderInputLayout->Release();
	m_shaderInputLayout = nullptr;
	if (m_coloredLineShaderInputLayout ) m_coloredLineShaderInputLayout->Release();
	m_coloredLineShaderInputLayout = nullptr;
	if( m_lineVertexShader) m_lineVertexShader->Release();
	m_lineVertexShader = nullptr;
	if( m_linePixelShader ) m_linePixelShader->Release();
	m_linePixelShader = nullptr;
	if( m_shadowMapVertexShader ) m_shadowMapVertexShader->Release();
	m_shadowMapVertexShader = nullptr;
	if( m_shadowMapPixelShader ) m_shadowMapPixelShader->Release();
	m_shadowMapPixelShader = nullptr;
	if( m_vertexShader ) m_vertexShader->Release();
	m_vertexShader = nullptr;
	if( m_pixelShader ) m_pixelShader->Release();
	m_pixelShader = nullptr;
	if( m_depthStencilBuffer ) m_depthStencilBuffer->Release();
	m_depthStencilBuffer = nullptr;
	if( m_standartRasterizerState ) m_standartRasterizerState->Release();
	m_standartRasterizerState = nullptr;
	if( m_wireFrameRasterizerState ) m_wireFrameRasterizerState->Release();
	m_wireFrameRasterizerState = nullptr;
	if( m_depthStencilView ) m_depthStencilView->Release();
	m_depthStencilView = nullptr;
	if( m_renderTargetView ) m_renderTargetView->Release();
	m_renderTargetView = nullptr;
	if( m_swapChain ) m_swapChain->Release();
	m_swapChain = nullptr;
	if( m_d3dContext ) m_d3dContext->Release();
	m_d3dContext = nullptr;
	if( m_d3dDevice ) m_d3dDevice->Release();	
	m_d3dDevice = nullptr;
	m_shadowMap1Renderer.ClearD3d();
	m_shadowMap2Renderer.ClearD3d();
}

void increment_msaa(ID3D11Device* device, UINT& count, UINT& quality)
{
	UINT numQualityLevels;
	if(device->CheckMultisampleQualityLevels(TEXTURE_FORMAT, count, &numQualityLevels) != S_OK)
	{
		return;
	}
	else if(quality < numQualityLevels - 1)
	{
		++quality;
	}
	else
	{
		quality = 0;
		numQualityLevels = 0;
		while (numQualityLevels == 0 && count < 100)
		{
			++count;
			if(device->CheckMultisampleQualityLevels(TEXTURE_FORMAT, count, &numQualityLevels) != S_OK)
			{
				count = 1;
				return;
			}
		}
		if(numQualityLevels == 0)
		{
			count = 1;
		}
	}
}

void GraphicsRenderer::StepAntiAliasingMode( void )
{
	increment_msaa(m_d3dDevice, m_antiAliasingCount, m_antiAliasingQuality);
	CString message;
	message.Format(L"Antialiasing changed to: sampling count=%d, quality=%d", m_antiAliasingCount, m_antiAliasingQuality);
	ShowInfo(message);
	m_RestartD3d();
}

void GraphicsRenderer::ShowInfo( LPCWSTR info )
{
	MessageBox( nullptr,
		info,
		L"Info",
		MB_OK );
}

void GraphicsRenderer::ToggleAntiAliasing( void )
{
	m_antiAliasing = !m_antiAliasing;
	m_RestartD3d();
}

void GraphicsRenderer::m_RestartD3d( void )
{
	m_ClearD3D();
	m_InitializeD3D();
	std::vector<Model*>::const_iterator mit;
	for(mit = m_models.begin(); mit != m_models.end(); ++mit)
	{
		(*mit)->ClearD3d();
		(*mit)->InitializeD3D(m_d3dDevice);
	}
	std::vector<Texture*>::const_iterator tit;
	for(tit = m_textures.begin(); tit != m_textures.end();  ++tit)
	{		
		(*tit)->ClearD3d();
		(*tit)->InitializeD3D(m_d3dDevice);
	}
	if(m_ray)
	{
		m_ray->ClearD3d();
		m_ray->InitializeD3D(m_d3dDevice);
	}
	if(m_kdTree)
	{
		m_kdTree->ClearD3d();
		m_kdTree->InitializeD3D(m_d3dDevice);
	}
	if(m_measurementLine)
	{
		m_measurementLine->ClearD3d();
		m_measurementLine->InitializeD3D(m_d3dDevice);
	}
}

#undef COMPILE_COLORED_LINE_SHADERS